﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

namespace DanMuHelper.Cache
{
    /// <summary>
    /// 粒子特效缓存接口, 返回的实例销毁原则：超过缓存池上线，超过很长时间没有使用的（需要有一个保底？）
    /// </summary>
    public class CacheParticleSystem_Auto
    {
        /// <summary>
        /// 预置体
        /// </summary>
        public ParticleSystem origin;

        /// <summary>
        /// 预制体原始缩放
        /// </summary>
        public Vector3 originScale = default;

        /// <summary>
        /// 缓存容量上限
        /// </summary>
        public int capacity = 16;

        private readonly HashSet<ParticleSystem> cacheParticles;
        private readonly Stack<ParticleSystem> fxPool;
        private readonly Queue<ParticleSystem> fxAlive;

        public CacheParticleSystem_Auto(int capacity, ParticleSystem prefab)
        {
            this.capacity = capacity;
            this.origin = prefab;
            originScale = prefab.transform.localScale;
            fxPool = new(capacity);
            fxAlive = new(capacity);
            cacheParticles = new(capacity);
        }

        /// <summary>
        /// 创建粒子实例
        /// </summary>
        public ParticleSystem GetOrCreate(Vector3 position)
        {
            ParticleSystem result = null;
            if (fxPool.Count > 0)
            {
                result = fxPool.Pop();
                result.transform.position = position;
                result.gameObject.SetActive(true);
                cacheParticles.Remove(result);
            }
            else result = CreateNewParticle(origin, position, origin.transform.rotation);

            fxAlive.Enqueue(result);
            return result;
        }

        /// <summary>
        /// 创建粒子实例
        /// </summary>
        public ParticleSystem GetOrCreate(Vector3 position, Quaternion rotation)
        {
            ParticleSystem result = null;
            if (fxPool.Count > 0)
            {
                result = fxPool.Pop();
                result.transform.SetPositionAndRotation(position, rotation);
                result.gameObject.SetActive(true);
                cacheParticles.Remove(result);
                return result;
            }
            else result = CreateNewParticle(origin, position, rotation);

            fxAlive.Enqueue(result);
            return result;
        }

        /// <summary>
        /// 返还粒子实例
        /// </summary>
        private void Return(ParticleSystem target)
        {
            if (cacheParticles.Contains(target))
            {
                // an error
                // SLog.Error("Particle already in cache!");
                return;
            }

            // TODO: #lich# 验证返还的对象是否是通过缓存接口创建的？
            if (fxPool.Count >= capacity)
            {
                DestroyParticle(target);
                return;
            }

            // TODO: #lich# 回收策略：直接禁用（依赖与PlayOnAwake属性）; 停止播放并移动到相机视野以外
            target.gameObject.SetActive(false);
            target.transform.localScale = originScale;

            fxPool.Push(target);
            cacheParticles.Add(target);
        }

        /// <summary>
        /// 缓存池修剪，需要定期调用一次
        /// </summary>
        public void Update()
        {
            int count = fxAlive.Count;
            if (count > 1)
            {
                for (int i = 0; i < count; i++)
                {
                    var ps = fxAlive.Peek();
                    if (!ps.gameObject.activeSelf)
                    {
                        fxAlive.Dequeue();
                        Return(ps);
                    }
                    else break;
                }
            }
        }

        ParticleSystem CreateNewParticle(ParticleSystem origin, Vector3 position, Quaternion rotation)
        {
            var result = UnityEngine.Object.Instantiate(origin, position, rotation);

            var main = result.main;
            main.stopAction = ParticleSystemStopAction.Disable;
            if (result.gameObject.activeSelf)
            {
                result.gameObject.SetActive(true);
            }
            return result;
        }

        void DestroyParticle(ParticleSystem ps)
        {
            UnityEngine.Object.Destroy(ps.gameObject);
        }
    }
}
